const fs = require('fs');
const fsp = require('fs').promises;
const path = require('path');
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const config = require('./config.js');
const DEB = config.debLog;
const logger = require('./Logger.js'); // 引入日志记录模块
const UserData = require('./UserData.js');
const ttUtils = require('./TTUtils.js');
const { getEcpmData } = require('./ecpmApi.js');
const app = express();

// 配置加载器（用于后台管理API）
const configLoader = require('./configLoader.js');

// 创建一个对象来存储玩家的数据
let tt_sessionStore = {};
let test_whiteList = ['_000_iVO2tcrELaWpTa-mAYxwGbAUwqVaSnQ', '_000-ffT9nYHCnwn4lOzSjPdZcW_-nsXDfHX', '_000FNx1RcqQnwkYzKNq-cKtLGBf-mjMcqP3', '_000sfDRL1hjACri5MDbbn-kc0OEIn74Qt5K'];
const ttSessionPath = 'static/tt-session.json';
const feedBackPath = 'static/a-feedBack.json';
const gameConfigPath = 'static/GameConfig.json';


app.use(cors({ origin: '*', allowedHeaders: '*', methods: '*' }));
app.use('/static', express.static('static'));
// 添加中间件来处理找不到文件的情况
app.use('/static', (req, res, next) => {
	const filePath = path.join(__dirname, 'static', req.url);
	// console.log(`进入游戏，请求的文件路径：${filePath}`);
	fs.access(filePath, fs.constants.F_OK, (err) => {
		if (err) {
			// 文件不存在，返回内容为字符串{}
			res.json({});
		} else {
			// 文件存在，继续处理
			next();
		}
	});
});
app.use('/up', express.raw({ type: '*/*' }));
app.use(express.json({ limit: '1mb' })); // 解析 application/json 类型的请求体

app.post('/getEcpmData', getEcpmData);

app.post('/up', (req, resp) => {
	function error(reason) {
		resp.json({ success: false, reason });
	}
	const filename = req.headers['filename'];
	// console.log(`保存进度，请求文件名：${filename}`);
	if (!filename || typeof filename !== 'string') {
		error('no filename');
		return;
	}
	if (!isValidFilename(filename)) {
		error('invalid filename');
		return;
	}

	if (!req.body) {
		error('no body');
		return;
	}

	if (!Buffer.isBuffer(req.body)) {
		error('invalid body');
		return;
	}

	let content = req.body.toString();
	try {
		fs.writeFileSync(`static/${filename}.json`, req.body);
		const timestr = new Date().toLocaleString();
		DEB && console.log(`${timestr}-success save file: ${filename}.json`);
	}
	catch (e) {
		error('error save file');
		//config.error(e);
		console.error(`failed to save file: ${filename}.json`);
		return;
	}
	resp.json({ success: true });

	ttUtils.onUpdateHoldRewardCoolingTime(filename, content, tt_sessionStore);
});


function isValidFilename(s) {
	if (s.length < 4 || s.length > 100) return false;
	return true;///^[a-zA-Z0-9_-]+$/.test(s);
}

app.post('/getYouyiCpUid', async (req, res) => {
	const urlParams = req.body?.urlParams;
	let uri = "/api/mobile/v1/cp/user";
	let url = `https://plat.gameyisi.com${uri}?${urlParams}`;
	// console.log("getYouyiCpUid url=" + url);
	try {
		const response = await axios.get(url);
		const result = response.data;
		DEB && console.log("getYouyiCpUid=", result);
		if (result && result.data) {
			let cpuid = result.data?.cp_uid;
			res.json({ cp_uid: cpuid });

			if (cpuid != "") {
				let userData = tt_sessionStore[cpuid];
				if (userData) {
					userData.updateActiveTime();
				}
				else {
					userData = new UserData(cpuid);
				}
				tt_sessionStore[cpuid] = userData;
			}
		} else {
			console.error(`Error getYouyiCpUid: ${result}`);
			res.status(400).json({ error: 'Failed to getYouyiCpUid', details: result });
		}
	} catch (error) {
		console.error(`Error making request to ${url}: ${error}`);
		res.status(500).json({ error: 'Internal Server Error', details: error });
	}
});

// 新增快手OpenID接口
app.get('/getKuaishouOpenID', async (req, res) => {
	const { code, package } = req.query;
	let APPID = config.ksAppId;
	let SECRET = config.ksSecret;
	if (package != "") {
		const data = config.apps[package];
		if (data) {
			APPID = data.AppId;
			SECRET = data.Secret;
		}
	}
	const url = `https://open.kuaishou.com/game/minigame/jscode2session?app_id=${APPID}&app_secret=${SECRET}&js_code=${code}&grant_type=authorization_code`;
	let openId = "";
	try {
		const response = await axios.get(url);
		const data = response.data;
		if (data && data.open_id) {
			openId = data.open_id;
			res.json({ open_id: openId });
		} else {
			console.error(`Error getting open_id: ${data}`);
			res.status(400).json({ error: 'Failed to getKuaishouOpenID', details: data });
		}
	} catch (error) {
		console.error(`Error making request to ${url}: ${error}`);
		res.status(500).json({ error: 'Internal Server Error', details: error });
	}

	if (openId != "") {
		let userData = tt_sessionStore[openId];
		if (userData) {
			userData.updateActiveTime();
		}
		else {
			userData = new UserData(openId);
		}
		tt_sessionStore[openId] = userData;
	}
});

// 新增微信小游戏OpenID接口
app.get('/getWeiXinOpenID', async (req, res) => {
	const { code, package } = req.query;
	let APPID = config.wxAppId;
	let SECRET = config.wxSecret;
	if (package) {
		const data = config.apps[package];
		if (data) {
			APPID = data.AppId;
			SECRET = data.Secret;
		}
	}
	const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`;
	let openId = "";

	try {
		const response = await axios.get(url);
		const data = response.data;
		if (data) {
			openId = data.openid;
			res.json({
				"openid": openId
			});
		} else {
			console.error(`微信接口返回异常：${JSON.stringify(data)}`);
			res.status(400).json({ error: '微信授权失败', errcode: data.errcode });
		}
	} catch (error) {
		console.error(`请求微信接口失败：${error}`);
		res.status(500).json({ error: '服务器内部错误' });
	}

	let userData = tt_sessionStore[openId];
	if (userData) {
		userData.updateActiveTime();
	}
	else {
		userData = new UserData(openId);
	}
	tt_sessionStore[openId] = userData;
});


// 新增抖音OpenID接口
app.get('/getDouyinOpenID', async (req, res) => {
	const { code, package } = req.query;
	let APPID = config.ttAppId;
	let SECRET = config.ttSecret;
	let needReqToken = true;
	if (package) {
		const data = config.apps[package];
		if (data) {
			APPID = data.AppId;
			SECRET = data.Secret;
			needReqToken = false;//这个appid的小游戏很多抖音接口暂时没有接入，所以token不需要；
		}
	}
	const url = `https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session?appid=${APPID}&secret=${SECRET}&code=${code}`;
	DEB && console.log(`获取${package}抖音openid url=${url}`);
	let openId = "";
	try {
		const response = await axios.get(url);
		const data = response.data;
		if (data && data.openid) {
			openId = data.openid;
			// logger.logMessage(`appid=${APPID},${openId}获取openId成功`);
			res.json({
				openid: openId
			});
		} else {
			logger.logMessage(`package=${package},appid=${APPID},${openId} 获取openId失败：${JSON.stringify(data)}`);
			res.status(400).json({ error: '抖音授权失败', errcode: data.errcode });
		}
	} catch (error) {
		logger.logMessage(`package=${package},appid=${APPID},${openId} 请求抖音接口失败：${error}`);
		res.status(500).json({ error: '服务器内部错误' });
	}

	let userData = tt_sessionStore[openId];
	if (!userData) {
		userData = new UserData(openId);
	}
	userData.updateActiveTime();
	tt_sessionStore[openId] = userData;
	if (needReqToken) {
		// 获取token
		await ttUtils.requestAccessToken();
	}
});
// 抖音更新玩家的订阅状态
app.get('/onNoticeUserSubscribeResult', async (req, res) => {
	const { openId } = req.query;
	const { flag } = req.query;
	let userData = tt_sessionStore[openId];
	if (!userData) {
		userData = new UserData(openId);
	}
	userData.setHadSubscribe(flag);
	tt_sessionStore[openId] = userData;
	logger.logMessage(`[${openId}]抖音订阅消息结果：${flag}`);
	res.json({
		res: 'success'
	});
});
// 抖音，向用户app发送订阅的消息
app.get('/onSubscribeNotify', async (req, res) => {
	const { openId } = req.query;
	const { tmplid } = req.query;
	const { desc } = req.query;

	let resetCount = 0;
	// 先判断是否有token
	let needReqToken = false;
	let userData = tt_sessionStore[openId];
	if (userData) {
		if (userData.hadSubscribe) {
			needReqToken = true;
		}
		else {
			needReqToken = false;
			logger.logMessage(`[${openId}]没有订阅消息成功，hadSubscribe=${userData.hadSubscribe}`);
			res.json({ res: 'fail', errorCode: 'subscribeFail' });
			return;
		}
	}
	else {
		needReqToken = false;
		logger.logMessage(`[${openId}]tt_sessionStore列表中没有它的数据`);
		res.json({ res: 'fail', errorCode: 'notSubscribe' });
		return;
	}
	if (needReqToken) {
		await ttUtils.requestAccessToken();
	}

	// 发送订阅消息
	let reqfun = async () => {
		let params = {
			access_token: ttUtils.getToken(),
			app_id: config.ttAppId,
			open_id: openId,
			tpl_id: tmplid,
			data: JSON.parse(desc)
		};
		let reqstr = JSON.stringify(params);
		let subscribeUrl = 'https://minigame.zijieapi.com/mgplatform/api/apps/subscribe_notification/developer/v1/notify';
		try {
			let subscribeResponse = await axios.post(subscribeUrl, params);
			// 返回成功响应
			let responsestr = JSON.stringify(subscribeResponse.data);
			logger.logMessage(`[${openId}]服务器发送订阅消息的参数：${reqstr}；返回的消息是：${responsestr}`);
			if (subscribeResponse.data.err_no == 0) {
				res.json({ res: '订阅通知已经发送' });
			}
			else if (subscribeResponse.data.err_no == 1001 && resetCount < 1) {
				await ttUtils.requestAccessToken(true);
				resetCount++;
				setTimeout(reqfun, 2000);
			}
			else {
				res.json({ res: subscribeResponse.data });
			}
		}
		catch (error) {
			logger.logMessage(`[${openId}]请求订阅消息接口失败：${error}`);
			res.json({ res: error });
		}
	};
	reqfun();
});

/**
 * 提供给抖音平台，用于查询离线奖励
 */
app.get('/onCheckOutLineReward', async (req, res) => {
	const params = req.query;
	const { openid, appid } = req.query;
	const signature = req.headers['x-signature'];
	let paramstr = JSON.stringify(params);
	logger.logMessage(`抖音平台查询离线奖励，收到的请求参数是:paramstr=${paramstr}`);
	let ttcontentid = config.ttContent_id;
	if (appid == 'ttd593a32e4a9a97f502') {
		// ttcontentid
	}
	// 验证签名的正确性，确保请求来自抖音开放平台
	let sig = ttUtils.getSignature(params, '');
	let xiangyindata;
	if (sig == signature) {
		// 白名单，直接返回true
		let flag = false;
		if (test_whiteList.indexOf(openid) > -1) {
			flag = true;
		}
		else {
			let userData = tt_sessionStore[openid];
			if (userData) {
				// logger.logMessage(`[${openid}]的冷却时间是：${userData.holdRewardCooling}`);
				flag = ttUtils.onCheckHoldReward(userData.holdRewardCooling);
			}
			else {
				// logger.logMessage(`[${openid}]的userData数据为空`);
			}
		}
		logger.logMessage(`[${openid}]是否可以领取离线奖励：${flag}`);
		if (flag) {
			//有奖励可以领取
			xiangyindata = {
				"err_no": 0,
				"err_msg": "",
				"data": {
					"scenes": [{
						"scene": 1,
						"content_ids": [ttcontentid],
						"extra": ""
					}]
				}
			}
		}
		else {
			xiangyindata = {
				"err_no": 0,
				"err_msg": "",
				"data": {
					"scenes": []
				}
			}
		}
	}
	else {
		logger.logMessage(`[${openid}]抖音平台查询离线奖励，签名不正确。平台的签名是${signature}, 我们计算的签名是${sig}`);
		xiangyindata = {
			"err_no": 28006009,
			"err_msg": "check signature failed",
			"data": {
				"scenes": []
			}
		}
	}
	//
	let bodystr = JSON.stringify(xiangyindata);
	let backSig = ttUtils.getSignature(params, bodystr);
	// 构建返回数据的响应头信息Header，要包括content-type,x-signature 
	// 设置响应头
	res.set('content-Type', 'application/json');
	res.set('x-signature', backSig);

	// 发送响应
	res.json(xiangyindata);
});
// 查询当前服务器内存中openId的数据
app.get('/onQueryUserData', async (req, res) => {
	const { openId } = req.query;
	let userData = tt_sessionStore[openId];
	res.json({
		'res': 'success',
		'data': userData
	});
});
// 记录反馈的信息
app.get('/onReportUserFeedBack', async (req, res) => {
	const { feedstr } = req.query;
	const timestr = new Date().toLocaleString();
	const message = `[${timestr}] ${feedstr}\n`;

	// 追加日志
	await fsp.appendFile(feedBackPath, message);
	try {
		// 检查文件是否存在并获取其大小
		let stats;
		try {
			stats = await fsp.stat(feedBackPath);
		} catch (err) {
			// 如果文件不存在，初始化一个空文件
			await fsp.writeFile(feedBackPath, '');
			stats = { size: 0 };
		}

		// 判断是否超过 2MB（2 * 1024 * 1024 bytes）
		if (stats.size > 2 * 1024 * 1024) {
			const dateSuffix = ttUtils.formatTime("yyyy-MM-dd-HH-mm-ss");
			const backupFileName = feedBackPath.replace('.json', `-${dateSuffix}.json`);

			// 备份原文件
			await fsp.rename(feedBackPath, backupFileName);

			// 创建新的空文件
			await fsp.writeFile(feedBackPath, '');
		}

		res.json({
			'res': 'success',
			'time': timestr
		});
	} catch (e) {
		console.error('Failed to write feedback:', e);
		res.json({
			'res': 'fail',
			'time': timestr
		});
	}
});
// 记录游戏来源
app.get('/onReportYouXiFrom', async (req, res) => {
	const { mesg } = req.query;
	logger.writeFromMeg(mesg);
	res.json({
		'res': 'success'
	});
});
// 获取广告ecpm
app.get('/onGetUserEcpm', async (req, res) => {
	// const { openId } = req.query;
	let openId = "_000UXODdHv_4lqlSTqZemHoo9wtZj2_sRNb";//"_000HWp4vSyfzGDnmGhhRwLVDO9LdIG7I0YK";
	// // 判断是否广告激活用户
	const appid = config.ttAppId;
	let timestr = new Date().toLocaleString();
	let restr = "";

	// 先判断token是否有效
	await ttUtils.requestAccessToken();
	let token = ttUtils.getToken();
	if (!token || token.length == 0) {
		DEB && console.log(`获取token失败`);
		res.json({
			'res': 'fail'
		});
		return;
	}
	let hour = 0;
	let dateStr = "";
	let pagenum = 1;
	let reqfun = async (dateStr, pagenum) => {
		const ecpmUrl = `https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm?open_id=${openId}&mp_id=${appid}&date_hour=${dateStr}&access_token=${token}&page_no=${pagenum}&page_size=500`;
		try {
			const ecpmResp = await axios.get(ecpmUrl);
			let ecpmRespData = ecpmResp.data;
			let err_no = ecpmRespData.err_no;
			if (err_no == 0) {
				restr = 'success';
				let records = ecpmRespData.data.records;
				DEB && console.log(`[${dateStr}] openId=${openId} pagenum=${pagenum} 获取广告ecpm的返回数据有：${records.length}条数据`);
				if (records.length > 0) {
					DEB && console.log(JSON.stringify(records));
				}
				const ecmpLogPath = "static/ecmpLog.json";
				for (const record of records) {
					await fsp.appendFile(ecmpLogPath, JSON.stringify(record) + '\n');
				}
				if (records.length >= 500) {
					pagenum++;
					setTimeout(async () => {
						await reqfun(dateStr, pagenum);
					}, 1000);
				}
				else {
					if (hour < 23) {
						hour++;
						pagenum = 1;
						dateStr = "2025-08-11 " + (hour < 10 ? "0" + hour : hour);
						setTimeout(async () => {
							await reqfun(dateStr, pagenum);
						}, 1200);
					}
				}
			}
			else {
				restr = 'fail';
				let err_msg = ecpmRespData.err_msg;
				let log_id = ecpmRespData.log_id;
				DEB && console.log(`[${openId}]请求ecpm接口失败：${err_no}，err_msg=${err_msg}，log_id=${log_id}`);
			}
		} catch (error) {
			restr = 'fail';
			DEB && console.log(`[${openId}]请求ecpm接口失败：${error}`);
		}
	};
	dateStr = "2025-08-11 " + (hour < 10 ? "0" + hour : hour);
	await reqfun(dateStr, pagenum);
	res.json({
		'res': restr,
		'time': timestr
	});
});

app.get('/onGetActionUserInfo', async (req, res) => {
	const { openId } = req.query;
	const appid = config.ttAppId;

	// 先判断token是否有效
	await ttUtils.requestAccessToken();
	let token = ttUtils.getToken();
	if (!token) {
		DEB && console.log(`获取token失败`);
		return;
	}
	// Todo这里的时间是当前的前一天，需要修改；
	const dateStr = "2025-09-07";//ttUtils.formatTime("yyyy-MM-dd");

	const url = `https://minigame.zijieapi.com/mgplatform/api/apps/data/get_active_user_info?open_id=${openId}&mp_id=${appid}&date=${dateStr}&page_no=1&page_size=500`;
	DEB && console.log(`判断是否广告激活用户的参数是：${url}`);
	try {
		//把token的值添加到header中
		const config = {
			headers: {
				'access-token': token
			}
		};
		const resp = await axios.get(url, config);
		let respData = resp.data;
		DEB && console.log("respData=", JSON.stringify(respData));
		let err_no = respData.err_no;
		if (err_no == "0") {
			let dataReady = respData.data.data_ready;
			if (dataReady) {
				let userinfos = respData.data.active_user_infos;
				let userinfo = userinfos[0];
			}
			else {
				DEB && console.log(`[${openId}]数据依赖离线产出任务，请晚些时候来请求`);
			}
		}
		else {
			let err_msg = respData.err_msg;
			let log_id = respData.log_id;
			DEB && console.log(`[${openId}]请求get_active_user_info接口失败：${err_no}，err_msg=${err_msg}，log_id=${log_id}`);
		}
		res.json(respData);
		return;
	} catch (error) {
		DEB && console.log(`[${openId}]请求get_active_user_info接口失败：${error}`);
	}
	res.json({
		'res': 'fail'
	});
});
//-------最右SDK----------------------------
const zuiyou_Api = 'https://gamecenterapi.izuiyou.com/api/srv_data_report';
app.post('/onCheckZuiyouSign', async (req, res) => {
	const { uid, sign, ts } = req.body;
	const package = 'H5ChildGodRoadZuiyou';
	const data = config.apps[package];
	const appkey = data.AppId;
	const appsecret = data.Secret;
	let sign2 = ttUtils.getZuiyouSignature(appkey, appsecret, ts, uid);
	logger.logMessage(`[${uid}]最右平台，校验sign：传入的参数是${appkey}-${appsecret}-${ts}-${uid}；平台给的sign是:${sign}；我们计算的是：${sign2}`);
	if (sign2 == sign) {
		res.json({
			"code": 1,
			"msg": "success"
		});
	} else {
		res.json({
			"code": 0,
			"msg": "sign error"
		});
	}

	let encodeuid = uid
		.replace(/\//g, 'y-y')           // 替换斜杠
		.replace(/\\/g, '-y-')           // 替换反斜杠
		.replace(/:/g, 'yy-')            // 替换冒号
		.replace(/\*/g, '-yy')           // 替换星号
		.replace(/\?/g, '--y')           // 替换问号
		.replace(/"/g, "y0y")            // 替换双引号
		.replace(/</g, 'yy0')            // 替换小于号
		.replace(/>/g, '0yy')            // 替换大于号
		.replace(/\|/g, '0y0');           // 替换竖线
	let openId = "zy-" + encodeuid;
	let userData = tt_sessionStore[openId];
	if (userData) {
		userData.updateActiveTime();
	}
	else {
		userData = new UserData(openId);
	}
	tt_sessionStore[openId] = userData;
});

app.post('/onLoginIn', async (req, res) => {
	const { uid, userip } = req.body;
	const package = 'H5ChildGodRoadZuiyou';
	const cfg = config.apps[package];
	let param2 = {
		data_type: 'TYPE_ENTER_GAME',
		app: 'zuiyou',
		game_id: cfg.gameid,
		uid: uid,
		time: ttUtils.formatTime(),
		user_ip: userip,
		payload: '{"server_id": "1"}'
	};
	let sign2 = ttUtils.getZuiyouSignature2(param2, cfg.Secret);
	let params = {
		...param2,
		sign: sign2
	};
	let reqstr = JSON.stringify(params);
	try {
		let apiResponse = await axios.post(zuiyou_Api, params);
		// 返回成功响应
		let responsestr = JSON.stringify(apiResponse.data);
		logger.logMessage(`[${uid}]最右平台，用户进入游戏数据上报：${reqstr}；返回的消息是：${responsestr}`);
		if (apiResponse.data.code == 0) {
			res.json({ res: 'success' });
		}
		else {
			res.json({ res: apiResponse.data });
		}
	}
	catch (error) {
		logger.logMessage(`[${uid}]最右平台，用户进入游戏数据上报失败：${error}`);
		res.json({ res: error });
	}
});

app.post('/onGameStart', async (req, res) => {
	const { uid, userip, roleId, roleName, roleLevel } = req.body;
	let payloadobj = {
		server_id: "1",
		role_id: roleId + "",
		role_name: roleName,
		role_level: parseInt(roleLevel),
	};
	const package = 'H5ChildGodRoadZuiyou';
	const cfg = config.apps[package];
	let param2 = {
		data_type: 'TYPE_START_GAME',
		app: 'zuiyou',
		game_id: cfg.gameid,
		uid: uid,
		time: ttUtils.formatTime(),
		user_ip: userip,
		payload: JSON.stringify(payloadobj)
	};
	let sign2 = ttUtils.getZuiyouSignature2(param2, cfg.Secret);
	let params = {
		...param2,
		sign: sign2
	};
	let reqstr = JSON.stringify(params);
	try {
		let apiResponse = await axios.post(zuiyou_Api, params);
		// 返回成功响应
		let responsestr = JSON.stringify(apiResponse.data);
		logger.logMessage(`[${uid}]最右平台，用户游戏开始数据上报：${reqstr}；返回的消息是：${responsestr}`);
		if (apiResponse.data.code == 0) {
			res.json({ res: 'success' });
		}
		else {
			res.json({ res: apiResponse.data });
		}
	}
	catch (error) {
		logger.logMessage(`[${uid}]最右平台，用户游戏开始数据上报失败：${error}`);
		res.json({ res: error });
	}
});

app.post('/onCreateRole', async (req, res) => {
	const { uid, userip, roleId, roleName } = req.body;
	let payloadobj = {
		server_id: "1",
		role_id: roleId,
		role_name: roleName
	};
	const package = 'H5ChildGodRoadZuiyou';
	const cfg = config.apps[package];
	let params2 = {
		data_type: 'TYPE_CREATE_ROLE',
		app: 'zuiyou',
		game_id: cfg.gameid,
		uid: uid,
		time: ttUtils.formatTime(),
		user_ip: userip,
		payload: JSON.stringify(payloadobj)
	};
	let sign2 = ttUtils.getZuiyouSignature2(params2, cfg.Secret);
	let params = {
		...params2,
		sign: sign2
	};
	let reqstr = JSON.stringify(params);
	try {
		let apiResponse = await axios.post(zuiyou_Api, params);
		// 返回成功响应
		let responsestr = JSON.stringify(apiResponse.data);
		logger.logMessage(`[${uid}]最右平台，用户创建角色数据上报：${reqstr}；返回的消息是：${responsestr}`);
		if (apiResponse.data.code == 0) {
			res.json({ res: 'success' });
		}
		else {
			res.json({ res: apiResponse.data });
		}
	}
	catch (error) {
		logger.logMessage(`[${uid}]最右平台，用户创建角色数据上报失败：${error}`);
		res.json({ res: error });
	}
});


// 转发兑换码接口
app.get('/getDuiHuanMa', async (req, res) => {
	const { code, userid } = req.query;
	DEB && console.log("getduihuanma111");
	const url = `https://apidemo.ckbuyu.com/redeem?code=${code}&userid=${userid}`;
	try {
		const response = await axios.get(url);
		const data = response.data;
		res.json(data);
	} catch (error) {
		console.error(`Error getDuiHuanMa request to ${url}: ${error}`);
		res.status(500).json({ error: 'Internal Server Error', details: error });
	}
});


// ---GM----
// 查询当前服务器内存中的数据 tt_sessionStore
app.get('/onQuerySession', async (req, res) => {
	const timestr = new Date().toLocaleString();
	res.json({
		'res': 'success',
		'time': timestr,
		'data': tt_sessionStore,
		'token': ttUtils.getToken()
	});
});
// 把tt_sessionStore数据保存到文件
app.get('/onSaveSession', async (req, res) => {
	const timestr = new Date().toLocaleString();
	let content = JSON.stringify(tt_sessionStore);
	try {
		fs.writeFileSync(ttSessionPath, content);
		DEB && console.log(`${timestr}-success save file: tt-session.json`);
	}
	catch (e) {
		console.error(`failed to save file: tt-session.json`);
		res.json({
			'res': 'fail',
			'time': timestr
		});
		return;
	}
	res.json({
		'res': 'success',
		'time': timestr
	});
});
// 从文件中加载tt_sessionStore数据，当服务器重启后，执行此方法
app.get('/onLoadSession', async (req, res) => {
	const timestr = new Date().toLocaleString();
	// 从文件ttSessionPath中加载数据
	try {
		const content = fs.readFileSync(ttSessionPath, 'utf8');
		const parsedData = JSON.parse(content);

		// 将每个对象转换为UserData实例
		tt_sessionStore = {};
		for (const openId in parsedData) {
			if (parsedData.hasOwnProperty(openId)) {
				let userData = new UserData(openId, false);
				Object.assign(userData, parsedData[openId]);
				tt_sessionStore[openId] = userData;
			}
		}

		DEB && console.log(`${timestr}-success load file: tt-session.json`);
	} catch (e) {
		console.error(`failed to load file: tt-session.json`);
		res.json({
			'res': 'fail',
			'time': timestr
		});
		return;
	}
	res.json({
		'res': 'success',
		'time': timestr
	});
});
app.get('/onQueryActive', async (req, res) => {
	const { day } = req.query;
	// 查找出最近一次的活跃时间是在day天之前的数据
	const endtime = Date.now() - 24 * 60 * 60 * 1000 * day;
	let retdata = {};
	for (const openId in tt_sessionStore) {
		let userData = tt_sessionStore[openId];
		if (userData) {
			if (userData.activeTime > 0 && userData.activeTime <= endtime) {
				retdata[openId] = userData;
			}
		}
	}
	const timestr = new Date().toLocaleString();
	res.json({
		'res': 'success',
		'time': timestr,
		'data': retdata
	});
});
app.get('/onDeleActive', async (req, res) => {
	const { day } = req.query;
	// 查找出最近一次的活跃时间是在day天之前的数据
	const endtime = Date.now() - 24 * 60 * 60 * 1000 * day;
	for (const openId in tt_sessionStore) {
		let userData = tt_sessionStore[openId];
		if (userData) {
			if (userData.activeTime > 0 && userData.activeTime <= endtime) {
				// logger.logMessage(`[${openId}]删除过期的openId`);
				delete tt_sessionStore[openId];
			}
		}
	}
	const timestr = new Date().toLocaleString();
	res.json({
		'res': 'success',
		'time': timestr
	});
});
// 把内存中的log数据保存到文件
app.get('/onSaveLogImmedi', async (req, res) => {
	const timestr = new Date().toLocaleString();
	logger.onImmediatelySaveLog();
	res.json({
		'res': 'success',
		'time': timestr
	});
});

app.get('/queryLog', async (req, res) => {
	const timestr = new Date().toLocaleString();
	const { day } = req.query;
	let logData = await logger.onGetLog(day);
	res.json({
		'res': 'success',
		'time': timestr,
		'data': logData
	});
});

app.get('/onQueryFeedBack', async (req, res) => {
	const timestr = new Date().toLocaleString();
	try {
		const content = await fs.readFileSync(feedBackPath, 'utf8');
		res.json({
			'res': 'success',
			'time': timestr,
			'data': content
		});
	} catch (e) {
		res.json({
			'res': 'fail',
			'time': timestr
		});
		return;
	}
});
app.post('/onSaveGameConfig', (req, res) => {
	try {
		const configData = req.body;
		DEB && console.log(`保存游戏配置文件:${JSON.stringify(configData)}`);
		let path = gameConfigPath;
		if (!!configData.AppPackage)
			path = `static/${configData.AppPackage}/GameConfig.json`;
		fs.writeFile(path, JSON.stringify(configData, null, 2), err => {
			if (err) {
				console.error('写入配置文件失败:', err);
				return res.status(500).json({ res: 'fail', reason: '写入文件失败' });
			}

			DEB && console.log(`成功保存 ${path}`);
			return res.json({ res: 'success' });
		});
	} catch (e) {
		console.error('保存配置出错:', e);
		return res.status(400).json({ res: 'fail', reason: '保存失败' });
	}
});
// 重置玩家数据：1，删除session中的数据；2，删除保存的本地文件
app.get('/onResetPlayer', async (req, res) => {
	const { userId } = req.query;
	const timestr = new Date().toLocaleString();
	let findPlayer = false;
	for (const openId in tt_sessionStore) {
		let userData = tt_sessionStore[openId];
		if (userData) {
			let useropenid = userData.openId;
			if (useropenid == userId) {
				findPlayer = true;
				delete tt_sessionStore[openId];
				break;
			}
		}
	}
	// 删除static文件夹下，文件名中包含userId的文件
	if (findPlayer) {
		const staticDir = path.join(__dirname, 'static');
		try {
			const files = await fsp.readdir(staticDir);
			for (const file of files) {
				if (file.includes(userId)) {
					const filePath = path.join(staticDir, file);
					await fsp.unlink(filePath);
					// logger.logMessage(`Deleted file: ${filePath}`);
				}
			}
			// logger.logMessage(`success delete userId:${userId} data`);
			res.json({
				'res': 'success',
				'time': timestr
			});
		} catch (e) {
			logger.logMessage(`failed to delete files for userId:${userId}`, e);
			res.json({
				'res': 'fail',
				'time': timestr
			});
		}
	}
	else {
		res.json({
			'res': 'fail；玩家账号输入的不对',
			'time': timestr
		});
	}
});

app.get('/onGetGameFromData', async (req, res) => {
	const timestr = new Date().toLocaleString();
	let fromData = await logger.onGetFrom();
	res.json({
		'res': 'success',
		'time': timestr,
		'data': fromData
	});
});

// 获取IP归属地信息接口
app.get('/getIPLocation', async (req, res) => {
	let { ip } = req.query;
	if (!ip) {
		ip = req.ip;
		if (req.headers['x-forwarded-for']) {
			const ips = req.headers['x-forwarded-for'].split(',');
			ip = ips[0].trim();
		}
	}
	const isLocalIp = ip === '::1' || ip === '127.0.0.1';
	if (isLocalIp) {
		res.json({
			'msg': 'SUCCESS',
			'city': '本地主机',
			'clientIp': ip
		});
		return;
	}
	const cachedLocation = ipLocationCache.get(ip);
	if (cachedLocation) {
		res.json({
			'msg': 'SUCCESS',
			'city': cachedLocation.city,
			'clientIp': ip,
			'from_cache': true
		});
		return;
	}

	const useFirstApi = Math.random() > 0.5;
	let locationData = null;
	if (useFirstApi) {
		locationData = await tryApi1(ip);
		if (!locationData) {
			locationData = await tryApi2(ip);
		}
	} else {
		locationData = await tryApi2(ip);
		if (!locationData) {
			locationData = await tryApi1(ip);
		}
	}

	if (!locationData) {
		res.json({
			'msg': 'fail',
			'error': 'IP归属地查询失败',
			'clientIp': ip
		});
		return;
	}

	ipLocationCache.set(ip, locationData);
	res.json({
		'msg': 'SUCCESS',
		'city': locationData.city,
		'clientIp': ip,
		'api_source': locationData.api_source
	});
});
const ipLocationCache = {
	cache: new Map(),
	maxSize: 50000,
	ttl: 24 * 60 * 60 * 1000,
	set(ip, location) {
		if (this.cache.size >= this.maxSize) {
			const oldestKey = this.cache.keys().next().value;
			this.cache.delete(oldestKey);
		}
		this.cache.set(ip, {
			data: location,
			timestamp: Date.now()
		});
	},
	get(ip) {
		const cachedItem = this.cache.get(ip);
		if (!cachedItem) return null;
		if (Date.now() - cachedItem.timestamp > this.ttl) {
			this.cache.delete(ip);
			return null;
		}
		const data = cachedItem.data;
		this.cache.delete(ip);
		this.cache.set(ip, cachedItem);
		return data;
	}
};
// ip接口(ip-api.com)
async function tryApi1(ip) {
	try {
		const url = `http://ip-api.com/json/${ip}?lang=zh-CN`;
		const response = await axios.get(url);
		const data = response.data;
		if (data.status === 'success') {
			return {
				city: data.city,
				api_source: 'ip-api.com'
			};
		}
	} catch (error) {
		logger.logMessage(`ip-api.com接口获取IP归属地失败: ${error}`);
	}
	return null;
}
// ip接口(api.vore.top)
async function tryApi2(ip) {
	try {
		const url = `https://api.vore.top/api/IPdata?ip=${ip}`;
		const response = await axios.get(url);
		const data = response.data;
		if (data.code === 200 && data.adcode && data.adcode.c) {
			return {
				city: data.adcode.c,
				api_source: 'api.vore.top'
			};
		}
	} catch (error) {
		logger.logMessage(`api.vore.top接口获取IP归属地失败: ${error}`);
	}
	return null;
};

// 转发用户上报信息
app.post('/reportUserInfo', async (req, res) => {
	try {
		const { datastr } = req.body;
		if (!datastr) {
			return res.json({
				'res': 'fail',
				'error': '缺少datastr参数'
			});
		}
		// 构造上报URL并转发数据
		const USER_REPORT_URL = "https://apidemo5.ckbuyu.com/onReportYouXiFrom?mesg=";
		const reportUrl = USER_REPORT_URL + encodeURIComponent(datastr);
		try {
			await axios.get(reportUrl);
		} catch (error) {
			logger.logMessage(`上报用户信息失败：${error.message}`);
			return res.json({
				'res': 'fail',
				'error': error.message
			});
		}
		// 返回成功响应
		res.json({
			'res': 'success'
		});
	} catch (error) {
		logger.logMessage(`上报用户信息失败：${error.message}`);
		res.json({
			'res': 'fail',
			'error': error.message
		});
	}
});

// 配置管理API端点

// 获取所有配置
app.get('/getAppConfigs', (req, res) => {
	try {
		const configData = configLoader.getConfig();
		// 合并port配置，因为它在原始config.js中定义
		const fullConfig = {
			port: config.port,
			...configData
		};
		res.json(fullConfig);
	} catch (error) {
		logger.logMessage(`获取配置失败: ${error}`);
		res.status(500).json({ success: false, message: '获取配置失败' });
	}
});

// 更新全局配置
app.post('/updateAppConfigs', (req, res) => {
	try {
		const newConfig = req.body;
		const success = configLoader.updateConfig(newConfig);
		if (success) {
			// 重新加载配置以确保所有模块都能获取最新配置
			configLoader.loadConfig();
			res.json({ success: true, message: '配置更新成功' });
		} else {
			res.status(500).json({ success: false, message: '配置更新失败' });
		}
	} catch (error) {
		logger.logMessage(`更新配置失败: ${error}`);
		res.status(500).json({ success: false, message: '配置更新失败: ' + error.message });
	}
});

// 更新或添加应用配置
app.post('/updateAppConfig', (req, res) => {
	try {
		const { appName, appConfig } = req.body;
		if (!appName || !appConfig) {
			return res.status(400).json({ success: false, message: '缺少必要参数' });
		}
		const success = configLoader.updateAppConfig(appName, appConfig);
		if (success) {
			// 重新加载配置
			configLoader.loadConfig();
			res.json({ success: true, message: '应用配置更新成功' });
		} else {
			res.status(500).json({ success: false, message: '应用配置更新失败' });
		}
	} catch (error) {
		logger.logMessage(`更新应用配置失败: ${error}`);
		res.status(500).json({ success: false, message: '应用配置更新失败: ' + error.message });
	}
});

// 删除应用配置
app.post('/deleteAppConfig', (req, res) => {
	try {
		const { appName } = req.body;
		if (!appName) {
			return res.status(400).json({ success: false, message: '缺少应用名称参数' });
		}
		const success = configLoader.deleteAppConfig(appName);
		if (success) {
			// 重新加载配置
			configLoader.loadConfig();
			res.json({ success: true, message: '应用配置删除成功' });
		} else {
			res.status(500).json({ success: false, message: '应用配置删除失败' });
		}
	} catch (error) {
		logger.logMessage(`删除应用配置失败: ${error}`);
		res.status(500).json({ success: false, message: '应用配置删除失败: ' + error.message });
	}
});

// 批量删除应用配置
app.post('/batchDeleteAppConfigs', (req, res) => {
    try {
        const { appNames } = req.body;
        if (!appNames || !Array.isArray(appNames) || appNames.length === 0) {
            return res.status(400).json({ success: false, message: '缺少应用名称列表参数' });
        }
        const success = configLoader.batchDeleteAppConfigs(appNames);
        if (success) {
            // 重新加载配置
            configLoader.loadConfig();
            res.json({ success: true, message: `成功删除 ${appNames.length} 个应用配置` });
        } else {
            res.status(500).json({ success: false, message: '批量删除应用配置失败' });
        }
    } catch (error) {
        logger.logMessage(`批量删除应用配置失败: ${error}`);
        res.status(500).json({ success: false, message: '批量删除应用配置失败: ' + error.message });
    }
});

// 检查应用文件夹状态
app.post('/checkAppFolder', (req, res) => {
    try {
        const { appName } = req.body;
        if (!appName) {
            return res.status(400).json({ success: false, message: '缺少应用名称参数' });
        }
        
        const fs = require('fs');
        const path = require('path');
        
        const appFolderPath = path.join(process.cwd(), 'static', appName);
        const gameConfigPath = path.join(appFolderPath, 'GameConfig.json');
        
        const folderExists = fs.existsSync(appFolderPath);
        const configExists = fs.existsSync(gameConfigPath);
        
        res.json({ 
            success: true, 
            folderExists,
            configExists,
            message: folderExists ? '文件夹存在' : '文件夹不存在'
        });
    } catch (error) {
        logger.logMessage(`检查应用文件夹状态失败: ${error}`);
        res.status(500).json({ success: false, message: '检查应用文件夹状态失败: ' + error.message });
    }
});

// 游戏一些假接口，以保证游戏正常运行
app.post('/sync_xcx', (req, res) => {
    try {
        // 模拟成功响应
        res.status(200).json({
            status: 0,
            message: 'success',
            data: {}
        });
    } catch (error) {
        res.status(500).json({
            status: -1,
            message: '服务器错误',
            data: null
        });
    }
});
app.get('/config', (req, res) => {
    try {
        res.status(200).json({
            "code": 0,
            "data": {
                "server_timestamp": new Date().getTime(),
                "sync_batch_size": 30,
                "sync_interval": 30
            },
            "msg": ""
        });
    } catch (error) {
        res.status(500).json({
            "code": -1,
            "data": null,
            "msg": "服务器错误"
        });
    }
});

app.listen(config.port, () => {
	console.log(`server success listen to ${config.port}`);
	process.title = `NodeUserKeyValueStoreServer at ${config.port}`;
});

